package io.renren.wechat.service.token;

import io.renren.wechat.config.WechatProperties;
import io.renren.wechat.service.token.dto.AccessTokenResponse;
import io.renren.wechat.support.ResponseEntityUtils;
import io.renren.wechat.support.WechatConstants;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 区分三种类型access_token
 *
 * https://work.weixin.qq.com/api/doc/90001/90143/91201#%E5%8C%BA%E5%88%86%E4%B8%89%E7%A7%8D%E7%B1%BB%E5%9E%8Baccess_token
 */
@Component
public class AccessTokenService {
    private static final Logger LOGGER = LoggerFactory.getLogger(AccessTokenService.class);
    private RestTemplate fastRestTemplate;
    private StringRedisTemplate stringRedisTemplate;
    private WechatProperties wechatProperties;


    public String getSelfCorpAccessToken() {
        String accessTokenFromCache = getAccessTokenFromCache(wechatProperties.getCorpId());

        if (StringUtils.isBlank(accessTokenFromCache)) {
            final AccessTokenResponse accessToken = doGetSelfCorpAccessToken();

            cacheAccessToken(accessToken, wechatProperties.getCorpId());

            accessTokenFromCache = accessToken.getAccessToken();
        }

        return accessTokenFromCache;
    }

    private AccessTokenResponse doGetSelfCorpAccessToken() {
        final String getTokenUrl = String.format(WechatConstants.GET_TOKEN_URL, wechatProperties.getCorpId(), wechatProperties.getCorpSecret());
        LOGGER.debug("getting access token with url: [{}]", getTokenUrl);

        return this.doGetAccessToken(getTokenUrl, null);
    }

    public AccessTokenResponse doGetAccessToken(String url, Map<String, String> params) {
        ResponseEntity<AccessTokenResponse> responseEntity;
        if (MapUtils.isEmpty(params)) {
            responseEntity = fastRestTemplate.getForEntity(url, AccessTokenResponse.class);
        } else {
            responseEntity = fastRestTemplate.postForEntity(url, params, AccessTokenResponse.class);
        }
        LOGGER.debug("get access token response entity: {}", responseEntity);

        if (!responseEntity.getStatusCode().is2xxSuccessful()) {
            throw new RuntimeException("调用获取accessToken请求执行失败");
        }

        final AccessTokenResponse tokenResponse = responseEntity.getBody();
        if (tokenResponse == null) {
            LOGGER.warn("obtain access token failed, response body is empty!");
            throw new RuntimeException("获取accessToken失败，响应为空");
        }
        // Error-Code:"0", Error-Msg:"ok"
        boolean isError = ResponseEntityUtils.isError(responseEntity) || tokenResponse.isError();
        if (isError) {
            LOGGER.warn(">>>>>>>obtain access token failed, errMsg: {}", tokenResponse.getErrMsg());
            throw new RuntimeException("获取accessToken失败");
        }

        return tokenResponse;
    }

    public void cacheAccessToken(AccessTokenResponse accessToken, String corpid) {
        final String cacheKey = buildAccessTokenCacheKey(corpid);

        stringRedisTemplate.opsForValue().set(cacheKey, accessToken.getAccessToken(),
                accessToken.getExpiresIn(), TimeUnit.SECONDS);
    }

    public void cacheSuiteAccessToken(AccessTokenResponse accessToken, String corpid) {
        final String cacheKey = buildSuiteAccessTokenCacheKey(corpid);

        stringRedisTemplate.opsForValue().set(cacheKey, accessToken.getAccessToken(),
                accessToken.getExpiresIn(), TimeUnit.SECONDS);
    }

    public void cacheAuthCorpAccessToken(AccessTokenResponse accessToken, String corpid) {
        final String cacheKey = buildAuthCorpAccessTokenCacheKey(corpid);

        stringRedisTemplate.opsForValue().set(cacheKey, accessToken.getAccessToken(),
                accessToken.getExpiresIn(), TimeUnit.SECONDS);
    }

    public void cacheAccessToken(String accessToken, Integer expiresIn, String corpid) {
        final String cacheKey = buildAccessTokenCacheKey(corpid);

        stringRedisTemplate.opsForValue().set(cacheKey, accessToken, expiresIn, TimeUnit.SECONDS);
    }

    public String getAccessTokenFromCache(String corpId) {
        final String cacheKey = buildAccessTokenCacheKey(corpId);

        return stringRedisTemplate.opsForValue().get(cacheKey);
    }

    public String getAuthCorpAccessTokenFromCache(String corpId) {
        final String cacheKey = buildAuthCorpAccessTokenCacheKey(corpId);

        return stringRedisTemplate.opsForValue().get(cacheKey);
    }

    private String buildAccessTokenCacheKey(String corpid) {
        return String.format("access_token:%s", corpid);
    }

    private String buildAuthCorpAccessTokenCacheKey(String corpid) {
        return String.format("auth_access_token:%s", corpid);
    }

    private String buildSuiteAccessTokenCacheKey(String corpid) {
        return String.format("suite_access_token:%s", corpid);
    }

    @Autowired
    public void setFastRestTemplate(RestTemplate fastRestTemplate) {
        this.fastRestTemplate = fastRestTemplate;
    }

    @Autowired
    public void setStringRedisTemplate(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Autowired
    public void setWechatProperties(WechatProperties wechatProperties) {
        this.wechatProperties = wechatProperties;
    }
}
